#!/usr/bin/env python
#
# vmnetx - Virtual machine network execution
#
# Copyright (C) 2012-2014 Carnegie Mellon University
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.  A copy of the GNU General Public License
# should have been distributed along with this program in the file
# COPYING.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#

import argparse
from contextlib import closing
import libvirt
import sys

import vmnetx
from vmnetx.define import define_machine
from vmnetx.domain import DomainXML
from vmnetx.generate import generate_machine
from vmnetx.reference import PackageReference
from vmnetx.util import setup_libvirt

VERSION = '%(prog)s ' + vmnetx.__version__
DESCRIPTION = 'Construct a VMNetX virtual machine image.'

parser = argparse.ArgumentParser(description=DESCRIPTION)
parser.add_argument('--version', action='version', version=VERSION)
subparsers = parser.add_subparsers(title="Modes of operation")


def create(args):
    """ Create a new, empty virtual machine in virt-manager(1). After this,
    you can use virt-manager(1) to install and configure the VM.
    """
    define_machine(args.shortname, args.memory, args.disk,
            use_64bit=args.use_64bit)

subparser = subparsers.add_parser('create', description=create.__doc__,
    help="Create a blank VM and add it to virt-manager")
subparser.add_argument('shortname', metavar='short-name',
    help="virtual machine name")
subparser.add_argument('memory', type=int, help="allocated memory (MB)")
subparser.add_argument('disk', type=int, help="allocated disk (GB)")
subparser.add_argument('-w', '--64-bit', dest='use_64bit', action='store_true',
    help="Create 64-bit VM")
subparser.set_defaults(func=create)


def validate(args):
    """ Validate the domain XML of a virtual machine to ensure that it will be
    accepted by vmnetx(1).
    """
    data = args.domain_xml.read()
    with closing(libvirt.open('qemu:///session')) as conn:
        data = DomainXML.make_backward_compatible(conn, data)
    DomainXML(data, validate=DomainXML.VALIDATE_STRICT)

subparser = subparsers.add_parser('validate', description=validate.__doc__,
    help="Validate domain XML")
subparser.add_argument('domain_xml', metavar='domain-xml',
                       type=argparse.FileType('r'))
subparser.set_defaults(func=validate)


def package(args):
    """ Generate a VMNetX virtual machine package file, which should have
    a .nxpk extension. This package file can then be uploaded to a web server.
    """
    generate_machine(args.name, args.domain_xml, args.outfile,
                     compress=not args.uncompressed)

subparser = subparsers.add_parser('package', description=package.__doc__,
    help="Create a VMNetX package")
subparser.add_argument('-n', '--name', default='Virtual Machine',
    help="Name of virtual machine")
subparser.add_argument('-u', '--uncompressed', action="store_true",
    help="Skip compression of disk/memory images")
subparser.add_argument('domain_xml', metavar='domain-xml')
subparser.add_argument('outfile')
subparser.set_defaults(func=package)


def reference(args):
    """ Create a reference file linking to a remote VMNetX package.
    """
    ref = PackageReference(args.package_url)
    args.outfile.write(ref.xml)

subparser = subparsers.add_parser('reference', description=reference.__doc__,
    help="Create reference file linking to a VMNetX package")
subparser.add_argument('package_url', metavar='package-url')
subparser.add_argument('outfile', nargs='?', type=argparse.FileType('w'))
subparser.set_defaults(func=reference)

args = parser.parse_args()

try:
    setup_libvirt()
    args.func(args)
except KeyboardInterrupt:
    sys.exit(1)
except Exception, e:
    print str(e)
    detail = getattr(e, 'detail', None)
    if detail:
        print detail
    sys.exit(1)
